﻿#include "dlghistory.h"
#include "ui_dlghistory.h"
#include "globalstatic.h"
#include "globalsymbol.h"

#include <QDebug>
#include <QKeyEvent>
#include <QStandardPaths>
#include <QFileDialog>
#include <QDir>
#include <QCollator>
#include <QProgressDialog>
#include <QRandomGenerator>
#include <QTimer>
#include <QMessageBox>

DlgHistory::DlgHistory(QSettings* settings, QList<ChannelDescription> channelDescriptionList, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DlgHistory),
    m_settings(settings),
    m_channelDescriptionList(channelDescriptionList)
{
    ui->setupUi(this);

    // 初始化
    initUI();
    init();
}

DlgHistory::~DlgHistory()
{
    delete ui;
}

void DlgHistory::keyPressEvent(QKeyEvent *event)
{
    int key = event->key();
    Qt::KeyboardModifiers modifiers = event->modifiers();

    if (modifiers == Qt::ControlModifier || modifiers & Qt::ControlModifier) {
        // Ctrl键
        switch (key) {
        case Qt::Key_Q:
            this->close();
            break;
        default:
            break;
        }

    }

    QWidget::keyPressEvent(event);
}

void DlgHistory::initUI()
{
    setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::MSWindowsFixedSizeDialogHint);

    // 加载样式表
    loadStyle(":res/qss/style_Servo.qss");

    // 设置起止日期
    ui->calendarBegin->setDateRange(QDate(2000, 1, 1), QDate(2100, 1, 1));
    ui->calendarEnd->setDateRange(QDate(2000, 1, 1), QDate(2100, 1, 1));
    ui->calendarBegin->setSelectedDate(QDate::currentDate().addDays(-30));

    // 只读
    ui->lvwHistory->setEditTriggers(QListView::NoEditTriggers);
    // 屏蔽垂直滚动条, 像素滚动
    //ui->lvwHistory->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    //ui->lvwHistory->setVerticalScrollMode(QListView::ScrollPerPixel);
    //ui->lvwHistory->setUniformItemSizes(true);
    // 关联模型
    ui->lvwHistory->setModel(&m_historyModel);

    // 右键菜单: 导出
    QAction* actExport = new QAction(QStringLiteral("导出"), this);
    connect(actExport, &QAction::triggered, this, &DlgHistory::onExport);
    m_menu = new QMenu(this);
    m_menu->addAction(actExport);
    ui->lvwHistory->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(ui->lvwHistory, &QListView::customContextMenuRequested, this, &DlgHistory::onHistoryContextMenu);

    // 信号槽
    connect(ui->lvwHistory, &QListView::doubleClicked, this, &DlgHistory::onItemDoubleClicked);
    connect(ui->btnQueryAll, &QPushButton::clicked, this, &DlgHistory::onQueryAll);
    connect(ui->btnQuery, &QPushButton::clicked, this, &DlgHistory::onQuery);
    connect(ui->btnOk,  &QPushButton::clicked, this, &DlgHistory::onOk);
    connect(ui->btnCancel, &QPushButton::clicked, this, [=](){ this->reject(); });
}

void DlgHistory::init()
{
    // 存盘路径
    m_settings->beginGroup("Storage");
    QString savePath = m_settings->value("savePath").toString();
    m_settings->endGroup();
    m_savePath = savePath.prepend(qApp->applicationDirPath());
}

void DlgHistory::loadStyle(const QString &stylePath)
{
    QFile styleFile(stylePath);
    QTextStream styleStream(&styleFile);
    if(!styleFile.open(QIODevice::ReadOnly))
        return;

    QString style = styleStream.readAll();
    this->setStyleSheet(style);
    styleFile.close();
}

void DlgHistory::readDataFile(const QString &filename, QList<QByteArray> &bufferList)
{
    QFile dataFile(filename);
    if (!dataFile.open(QIODevice::ReadOnly))
        return;

    // 使用二进制方式读取文件
    qint64 size = dataFile.size();
    qint32 dataLength = GlobalStatic::getDataLength();
    for (qint64 i = 0; i < size; i += dataLength) {
        QByteArray buffer(dataLength, 0x0);
        dataFile.seek(i);
        dataFile.read(buffer.data(), dataLength);
        bufferList << buffer;
    }
    dataFile.close();
}

void DlgHistory::exportCsvFile(const QString &filename, const QList<QByteArray> &bufferList)
{
    QFile exportFile(filename);
    if (!exportFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
        QMessageBox::critical(this, QStringLiteral("错误"), QStringLiteral("导出数据文件打开失败!"));
        return;
    }
    QTextStream stream(&exportFile);

    // 第0行写入列标题
    stream << QStringLiteral("时间");
    for (int i = 0; i < CHANNEL_COUNT; i++)
        stream << "," << m_channelDescriptionList.at(i).name;
    stream << "\n";
    stream.flush();

    for (int i = 0; i < bufferList.size(); i++) {
        QByteArray buffer = bufferList.at(i);
        int bufferIndex = 0;

        // 时间戳
        qint64 timeStamp = 0;
        memcpy(&timeStamp, buffer.constData(), sizeof(qint64));
        stream << timeStamp;
        bufferIndex += sizeof(qint64);

        for (int i = 0; i < m_channelDescriptionList.size(); i++) {
            DataType dataType = GlobalStatic::getDataType(m_channelDescriptionList.at(i).dataType);
            switch (dataType) {
            case UC:
                stream << "," << static_cast<unsigned char>(buffer.at(bufferIndex));
                bufferIndex += sizeof(unsigned char);
                break;
            case SC:
                stream << "," << buffer.at(bufferIndex);
                bufferIndex += sizeof(char);
                break;
            case US: {
                int value = ((buffer.at(bufferIndex) & 0xFF) << 8) | (buffer.at(bufferIndex + 1) & 0xFF);
                stream << "," << value;
                bufferIndex += sizeof(unsigned short);
            }
                break;
            case SS: {
                int value = ((buffer.at(bufferIndex) & 0xFF) << 8) | (buffer.at(bufferIndex + 1) & 0xFF);
                stream << "," << value;
                bufferIndex += sizeof(short);
            }
                break;
            case UI: {
                int value = ((buffer.at(bufferIndex) & 0xFF) << 24) | ((buffer.at(bufferIndex + 1) & 0xFF) << 16) | ((buffer.at(bufferIndex + 2) & 0xFF) << 8) | (buffer.at(bufferIndex + 3) & 0xFF);
                stream << "," << value;
                bufferIndex += sizeof(unsigned int);
            }
                break;
            case SI: {
                int value = ((buffer.at(bufferIndex) & 0xFF) << 24) | ((buffer.at(bufferIndex + 1) & 0xFF) << 16) | ((buffer.at(bufferIndex + 2) & 0xFF) << 8) | (buffer.at(bufferIndex + 3) & 0xFF);
                stream << "," << value;
                bufferIndex += sizeof(int);
            }
                break;
            case UL: {
                unsigned long long value = 0;
                memcpy(&value, buffer.constData() + bufferIndex, sizeof(unsigned long long));
                stream << "," << value;
                bufferIndex += sizeof(unsigned long long);
            }
                break;
            case SL: {
                long long value = 0;
                memcpy(&value, buffer.constData() + bufferIndex, sizeof(long long));
                stream << "," << value;
                bufferIndex += sizeof(long long);
            }
                break;
            case F32:
            case F: {
                float value = 0;
                memcpy(&value, buffer.constData() + bufferIndex, sizeof(float));
                stream << "," << value;
                bufferIndex += sizeof(float);
            }
                break;
            case F64:
            case DBL: {
                double value = 0;
                memcpy(&value, buffer.constData() + bufferIndex, sizeof(double));
                stream << "," << value;
                bufferIndex += sizeof(double);
            }
                break;
            default:
                break;
            }
        }

        stream << "\n";
    }

    exportFile.close();
}

void DlgHistory::onQueryAll()
{
    QStringList history;

    // 找到存储路径下的所有dat文件
    QDir dir(m_savePath);
    QStringList filter;
    filter << "*.dat";
    dir.setNameFilters(filter);
    QFileInfoList fileInfoList = dir.entryInfoList(filter);
    for (int i = 0; i < fileInfoList.size(); i++) {
        QString fileName = fileInfoList.at(i).fileName();
        if (fileName.startsWith("data"))
            history << fileName;
    }

    if (history.empty()) {
        QMessageBox::information(this, QStringLiteral("提示"), QStringLiteral("未查询到有效数据文件!"));
        return;
    }

    // 倒序排列
    QCollator sorter;
    sorter.setNumericMode(true);
    sorter.setCaseSensitivity(Qt::CaseInsensitive);
    std::sort(history.begin(), history.end(), [&](const QString& a, const QString b) { return sorter.compare(a, b) > 0; });

    if (history.count() > REPORT_FILE_COUNT) {
        // 只取前面的行数: REPORT_FILE_COUNT
        int removeCount = history.count() - REPORT_FILE_COUNT;
        for (int i = 0; i < removeCount; i++)
            history.removeLast();
    }

    m_historyModel.setStringList(history);
}

void DlgHistory::onQuery()
{
    QDate beginDate = ui->calendarBegin->selectedDate();
    QDate endDate = ui->calendarEnd->selectedDate();

    if (beginDate > endDate) {
        beginDate = ui->calendarEnd->selectedDate();
        endDate = ui->calendarBegin->selectedDate();
    }

    QString beginFileName = QString("data_%1-00-00-00.dat").arg(beginDate.toString("yyyy-MM-dd"));
    QString endFileName = QString("data_%1-00-00-00.dat").arg(endDate.addDays(1).toString("yyyy-MM-dd"));

    QStringList history;

    // 找到存储路径下的所有dat文件
    QDir dir(m_savePath);
    QStringList filter;
    filter << "*.dat";
    dir.setNameFilters(filter);
    QFileInfoList fileInfoList = dir.entryInfoList(filter);
    for (int i = 0; i < fileInfoList.size(); i++) {
        QString fileName = fileInfoList.at(i).fileName();
        if (fileName.startsWith("data")) {
            if (QString::compare(fileName, beginFileName) > 0 && QString::compare(fileName, endFileName) < 0)
                history << fileName;
        }
    }

    if (history.empty()) {
        QMessageBox::information(this, QStringLiteral("提示"), QStringLiteral("未查询到所选时间范围内的有效数据文件!"));
        return;
    }

    // 倒序排列
    QCollator sorter;
    sorter.setNumericMode(true);
    sorter.setCaseSensitivity(Qt::CaseInsensitive);
    std::sort(history.begin(), history.end(), [&](const QString& a, const QString b) { return sorter.compare(a, b) > 0; });

    m_historyModel.setStringList(history);
}

void DlgHistory::onExport()
{
    QModelIndex modelIndex = ui->lvwHistory->currentIndex();
    if (modelIndex.row() == -1)
        return;

    QString dataFileName = QString("%1/%2").arg(m_savePath, m_historyModel.data(modelIndex).toString());
    QFileInfo fileInfo(dataFileName);

    //QString defaultFilePath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
    QString defaultFileName = QStringLiteral("%1/%2.csv").arg(m_savePath, fileInfo.baseName());
    QString defaultFileFilter = QStringLiteral("Csv File(*.csv)");
    QString exportFileName = QFileDialog::getSaveFileName(this, QStringLiteral("导出数据"), defaultFileName, defaultFileFilter);
    if (exportFileName.isEmpty())  // 取消保存
        return;

    // 读取二进制原始数据文件
    QList<QByteArray> bufferList;
    readDataFile(dataFileName, bufferList);
    if (bufferList.empty()) {
        QMessageBox::information(this, QStringLiteral("提示"), QStringLiteral("原始数据文件为空!"));
        return;
    }

    // 导出CSV数据文件
    exportCsvFile(exportFileName, bufferList);

    QMessageBox::information(this, QStringLiteral("提示"), QStringLiteral("数据文件(CSV)导出完成!"));
}

void DlgHistory::onHistoryContextMenu(const QPoint &pos)
{
    Q_UNUSED(pos);

    if (m_historyModel.rowCount() <= 0)
        return;

    m_menu->exec(QCursor::pos());
}

void DlgHistory::onItemDoubleClicked(const QModelIndex &modelIndex)
{
    QString fileName = m_historyModel.data(modelIndex).toString();
    //qDebug() << fileName;

    m_settings->beginGroup("Storage");
    m_settings->setValue("history", fileName.mid(5, 19));
    m_settings->endGroup();

    // 显示7.5s的进度条
//    QProgressDialog progressDialog;
//    progressDialog.setWindowModality(Qt::WindowModal);
//    progressDialog.setLabelText(QStringLiteral("正在查询历史数据..."));
//    progressDialog.setRange(0, 100);
//    for(int i = 0; i < 10; i++) {
//        if(progressDialog.wasCanceled())
//            break;
//        progressDialog.setValue(QRandomGenerator::global()->bounded(i * 10, (i + 1) * 10));

//        // 延时
//        QEventLoop eventloop;
//        QTimer::singleShot(750, &eventloop, &QEventLoop::quit);
//        eventloop.exec();
//    }
//    progressDialog.setValue(100);

    this->accept();
}

void DlgHistory::onOk()
{
    QModelIndex modelIndex = ui->lvwHistory->currentIndex();
    int row = modelIndex.row();
    if (row == -1)
        this->reject();

    QString fileName = m_historyModel.data(modelIndex).toString();
    //qDebug() << fileName;

    m_settings->beginGroup("Storage");
    m_settings->setValue("history", fileName.mid(5, 19));
    m_settings->endGroup();

    this->accept();
}
